package com.nulldev.util.web.nTube.pages;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Map;

import com.nulldev.util.VariableAPI.MathUtil;
import com.nulldev.util.codecs.uniapi.UniBase64;
import com.nulldev.util.data.Base64.Base64InputStream;
import com.nulldev.util.web.HttpServer3.Response;
import com.nulldev.util.web.HttpServer3.legacyPages.v1.RequestType;
import com.nulldev.util.web.HttpServer3.pages.v3.HttpPage3;
import com.nulldev.util.web.URLUtils.URLQueryParser;
import com.nulldev.util.web.URLUtils.URLQueryParser.URLQueryParserOptions;
import com.nulldev.util.web.URLUtils.URLUtils;
import com.nulldev.util.web.nTube.pages.misc.DJSCache;

public class Base64DecodingPage extends HttpPage3 {

	private static final int QUERY_LENGTH_LIMIT = 65536;
	private static final int REQUEST_BODY_BYTE_LIMIT = 4096;

	public Base64DecodingPage() {
		super("/b64_dec");
		super.setChecks(EnhancedSecurityChecks.ONLY_ALLOW_BASIC_METHODS);
		super.setCachingMethod(CachingMethod.NO_CACHE);
	}

	@Override
	public Response onRequest(final Request request) {
		final Map<String, String> s = URLQueryParser.extract(request.url(), URLQueryParserOptions.PREFER_EXPERIMENTAL,
				(URLUtils.USE_URL_ACCEL_V2 ? null : URLQueryParserOptions.DISABLE_URL_ACCEL), URLQueryParserOptions.TREAT_URL_AS_QUERY);
		final boolean checkBody = s.containsKey("dataInBody");
		final boolean forceDashMime = s.containsKey("dashMime");
		if (!s.containsKey("td") && !checkBody) {
			return new Response(400);
		} else if (checkBody && !request.hasInputStream()) {
			return new Response(400);
		} else if (checkBody) {
			return __do_new_body_path(request, s, forceDashMime);
		} else {
			return __do_legacy_query_path(request, s, forceDashMime);
		}
	}

	@SuppressWarnings("resource")
	private Response __do_new_body_path(final Request request, final Map<String, String> s, final boolean forceDashMime) {
		if (!request.isType(RequestType.POST)) {
			return new Response(400);
		}
		final long contentLength = MathUtil.safeParseL(request.header("Content-Length", "-1"), -1L);
		/*
		 * Reject missing Content-Lengths (already done) and very large request bodies
		 */
		if (contentLength == -1L || contentLength >= REQUEST_BODY_BYTE_LIMIT) {
			return new Response(400);
		}
		final InputStream data = request.getInputStream();
		final Base64InputStream output = new Base64InputStream(data);
		try {
			if (forceDashMime) {
				return new Response(200, output).setCustomMime("application/dash+xml").deferCompression(true);
			} else {
				return new Response(200, output).deferCompression(true);
			}
		} catch (Exception e) {
			return new Response(503);
		}
	}

	@SuppressWarnings("resource")
	private Response __do_legacy_query_path(final Request request, final Map<String, String> s, final boolean forceDashMime) {
		final boolean fromCache = s.containsKey("cached");
		final String td = s.get("td");
		/*
		 * Reject extremely long queries. (should be already blocked by HttpServer)
		 */
		if (td == null || td.length() >= QUERY_LENGTH_LIMIT) {
			return new Response(400);
		}
		if (fromCache) {
			final String m = (request.getType() == RequestType.HEAD ? DJSCache.getNoRemove(td) : DJSCache.get(td));
			if (forceDashMime) {
				return new Response(200, createStream(m)).header("Content-Length", m.length()).setCustomMime("application/dash+xml").deferCompression(true);
			} else {
				return new Response(200, createStream(m)).header("Content-Length", m.length()).deferCompression(true);
			}
		} else {
			final byte[] dec = UniBase64.decodeToBytes(td);
			if (forceDashMime) {
				return new Response(200, new ByteArrayInputStream(dec)).header("Content-Length", dec.length).setCustomMime("application/dash+xml")
						.deferCompression(true);
			} else {
				return new Response(200, new ByteArrayInputStream(dec)).header("Content-Length", dec.length).deferCompression(true);
			}
		}
	}

}
